#include <gtest/gtest.h>


#include "memdb/memdb.h"
#include "memdb/impl/mock/MockTable.h"
#include "memdb/error/MemdbError.h"

using namespace memdb;
using namespace memdb::impl::mock;

std::error_code success = std::error_code();

TEST(MockTest, use){
  ITable * table = new MockTable();
  ASSERT_TRUE(table);
  ASSERT_EQ(0u, table->count());

  auto && items = table->range(0, 10);
  ASSERT_TRUE(items);

  auto && fitems = table->findRange("key", 0, 10);
  ASSERT_TRUE(fitems);

  ASSERT_EQ(success, table->insert("key", "value"));
  ASSERT_EQ(1u, table->count());

  ASSERT_EQ(success, table->insert("key11", "value"));
  ASSERT_EQ(success, table->insert("key12", "value"));
  ASSERT_EQ(success, table->insert("key13", "value"));

  table->iterator()->foreach([](const ITable::Item & pair){
    std::cout << "k:" << pair->first  << ", v:" << pair->second << std::endl;
  });
    
  delete table;
}

class MockTableTest : public testing::Test {
protected:
  virtual void SetUp() override {
    table = new MockTable();

    ASSERT_EQ(success, table->insert("key11", "value"));
    ASSERT_EQ(success, table->insert("key12", "value"));
    ASSERT_EQ(success, table->insert("key13", "value"));
    ASSERT_EQ(success, table->insert("key14", "value"));
  }

  virtual void TearDown() override {
    delete table;
  }

  ITable * table;
};

TEST_F(MockTableTest, testInsert){
    ASSERT_EQ(success, table->insert("key15", "value"));

    ASSERT_EQ(memdb::error::key_exists, table->insert("key15", "value"));
}

TEST_F(MockTableTest, testRemove){
    ASSERT_EQ(success, table->remove("key14"));

    ASSERT_EQ(memdb::error::key_not_exists, table->remove("key14"));
}

TEST_F(MockTableTest, testRange){
  ASSERT_TRUE(table);
  ASSERT_EQ(2u, table->range(0, 2)->count());

  int index = 1;
  size_t len = 10;
  ASSERT_EQ(len < (table->count() - index)? len : (table->count() - index), table->range(index, len)->count());
}

TEST_F(MockTableTest, testRangeIndexOverSize){
  auto && it = table->range(table->count(), 10);
  ASSERT_TRUE(it);
  ASSERT_FALSE(it->next());
  ASSERT_EQ(0u, it->count());

  auto && it2 = table->range(table->count() + 1, 10);
  ASSERT_TRUE(it2);
  ASSERT_FALSE(it2->next());
  ASSERT_EQ(0u, it2->count());
}

TEST_F(MockTableTest, testUpdate){
  ASSERT_TRUE(table);

  ITable::ValueType value;
  ASSERT_EQ(success, table->update("key11", "value11", value));
  ASSERT_EQ("value", value);

  ASSERT_EQ(success, table->update("key12", "value12"));

  ASSERT_EQ(memdb::error::key_not_exists, table->update("key", "value"));

  ASSERT_EQ(memdb::error::key_not_exists, table->update("key10", "value", value));
}

TEST_F(MockTableTest, testReplaceExist){
  std::string key = "key11";
  std::string old_expect = "value";
  std::string new_expect = "value11";
  std::string value;
  
  auto && [ec, is_replace] = table->replace(key, new_expect, value);
  ASSERT_EQ(success, ec);
  ASSERT_TRUE(is_replace);
  ASSERT_EQ(old_expect, value);
  ASSERT_EQ(success, table->get(key, value));
  ASSERT_EQ(new_expect, value);
}

TEST_F(MockTableTest, testReplaceNotExist){
  std::string key = "key";
  std::string value_expect = "value";
  std::string value;

  auto && [ec, is_replace] = table->replace(key, value_expect, value);
  ASSERT_EQ(success, ec);
  ASSERT_FALSE(is_replace);
  ASSERT_EQ(success, table->get(key, value));
  ASSERT_EQ(value_expect, value);
}

TEST_F(MockTableTest, testFindRange){
  auto && it = table->findRange("key12", 0, 2);
  ASSERT_TRUE(it);
  ASSERT_EQ(2u, it->count());
}

TEST_F(MockTableTest, testFindRangeFirst){
  auto && it = table->findRange("key11", 0, 10);
  ASSERT_TRUE(it);
  ASSERT_EQ(table->count(), it->count());
}

TEST_F(MockTableTest, testFindRangeLast){
  auto && it = table->findRange("key14", 0, 10);
  ASSERT_TRUE(it);
  ASSERT_EQ(1u, it->count());
}

TEST_F(MockTableTest, testFindRangeLenOver){
  auto && it = table->findRange("key12", 0, 10);
  ASSERT_TRUE(it);
  ASSERT_EQ(3u, it->count());
}

TEST_F(MockTableTest, testFindRangeKeyNotExists){
  auto && it = table->findRange("key", 0, 10);
  ASSERT_TRUE(it);
  ASSERT_FALSE(it->next());
  ASSERT_EQ(0u, it->count());
}

TEST_F(MockTableTest, testFindRangeOffsetOversize){
  auto && it = table->findRange("key11", table->count(), 10);
  ASSERT_TRUE(it);
  ASSERT_FALSE(it->next());
  ASSERT_EQ(0u, it->count());

  auto && it2 = table->findRange("key11", table->count() + 1, 10);
  ASSERT_TRUE(it2);
  ASSERT_FALSE(it2->next());
  ASSERT_EQ(0u, it2->count());
}

TEST_F(MockTableTest, testExists){
  ASSERT_TRUE(table->exists("key13"));
  ASSERT_FALSE(table->exists("key15"));
}
